package com.texaco.chapasFutbol.entidades;

import static org.andengine.extension.physics.box2d.util.constants.PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.andengine.engine.Engine.EngineLock;
import org.andengine.engine.camera.Camera;
import org.andengine.engine.camera.ZoomCamera;
import org.andengine.engine.camera.hud.HUD;
import org.andengine.engine.handler.IUpdateHandler;
import org.andengine.engine.handler.timer.ITimerCallback;
import org.andengine.engine.handler.timer.TimerHandler;
import org.andengine.entity.Entity;
import org.andengine.entity.IEntity;
import org.andengine.entity.modifier.IEntityModifier.IEntityModifierListener;
import org.andengine.entity.modifier.LoopEntityModifier;
import org.andengine.entity.modifier.LoopEntityModifier.ILoopEntityModifierListener;
import org.andengine.entity.modifier.MoveModifier;
import org.andengine.entity.modifier.ScaleModifier;
import org.andengine.entity.modifier.SequenceEntityModifier;
import org.andengine.entity.primitive.Rectangle;
import org.andengine.entity.scene.IOnSceneTouchListener;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.scene.menu.MenuScene;
import org.andengine.entity.sprite.Sprite;
import org.andengine.entity.text.AutoWrap;
import org.andengine.entity.text.Text;
import org.andengine.entity.text.TextOptions;
import org.andengine.extension.physics.box2d.FixedStepPhysicsWorld;
import org.andengine.extension.physics.box2d.PhysicsConnector;
import org.andengine.extension.physics.box2d.PhysicsFactory;
import org.andengine.extension.physics.box2d.PhysicsWorld;
import org.andengine.input.touch.TouchEvent;
import org.andengine.opengl.font.Font;
import org.andengine.opengl.texture.region.ITextureRegion;
import org.andengine.util.HorizontalAlign;
import org.andengine.util.call.Callback;
import org.andengine.util.debug.Debug;
import org.andengine.util.modifier.IModifier;
import org.andengine.util.modifier.LoopModifier;
import org.json.JSONException;

import android.graphics.Point;
import android.graphics.Rect;
import android.opengl.GLES20;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.texaco.chapasFutbol.ChapasFutbolActivity;
import com.texaco.chapasFutbol.R;
import com.texaco.chapasFutbol.actions.CartelAction;
import com.texaco.chapasFutbol.camera.CameraHandler;
import com.texaco.chapasFutbol.camera.ScrollHandler;
import com.texaco.chapasFutbol.camera.ZoomHandler;
import com.texaco.chapasFutbol.menu.ContextualGameMenu;
import com.texaco.chapasFutbol.menu.EndGameMenu;
import com.texaco.chapasFutbol.menu.ExitGameMenu;
import com.texaco.chapasFutbol.menu.ExitOnlineMenu;
import com.texaco.chapasFutbol.multiplayer.Multiplayer;
import com.texaco.chapasFutbol.multiplayer.ServerUtilities;
import com.texaco.chapasFutbol.multiplayer.mensajes.AckLoginMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.BaseMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.IMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.MatchMessage;
import com.texaco.chapasFutbol.resources.Resources;
import com.texaco.chapasFutbol.scene.GameScene;
import com.texaco.chapasFutbol.scene.SceneHandler;

/**
 * Se trata del objeto que especificará las reglas del juego, qué equipo tiene
 * el turno actual y todas aquelas vbles que requieran el correcto desarrollo
 * del juego.
 * 
 * @author texaco
 * 
 */
public class Game implements IOnSceneTouchListener, IUpdateHandler{

//	TimerHandler th;
			
	/**
	 * TRUE si es el turno de las chapas locales.
	 */
	private boolean turnoLocal = true;

	/**
	 * Número de chapas de cada equipo.
	 */
	public static int NUM_CHAPAS_EQUIPO = 5;

	public final static Rect[] AREAS_GOL = { //FIXME: LLEVAR A RECURSOS DE ANDROID
			new Rect(1280 - 30, 0, 1280+500, 800),
			new Rect(-500, 0, 30, 800) };

	/*
	 * createFixtureDef(final float pDensity, final float pElasticity, final
	 * float pFriction)
	 * 
	 * El primer parámetro define la densidad. Este parámetro sólo tiene sentido
	 * para los objetos dinámicos, porque los cuerpos estáticos y cinéticos
	 * actuan como si tuvieran infinita masa/densidad.
	 * 
	 * El segundo parámetro define la elasticidad (o rebote). En otras palabras,
	 * cuánto rebota cuando los cuerpos chocan. Normalmente se define entre el 0
	 * para que no rebote a 1f para la maxima velocidad de reflexión.
	 * 
	 * El tercer parámetro define la fricción. Normalmente se define a partir 0f
	 * (sin fricción) a 1f para la fricción fuerte..
	 */
	final static FixtureDef wallFixtureDef = PhysicsFactory.createFixtureDef(0,
			1f, 0.5f, false, CFSprite.CAT_MURO, CFSprite.MASK_MURO, (short)0);
	final static FixtureDef goalFixtureDef = PhysicsFactory.createFixtureDef(0,
			1f, 0.5f, false, CFSprite.CAT_PORTERIA, CFSprite.MASK_PORTERIA, (short)0);

	private static final float VALOR_REBOTE_CHAPA = 2.0f;
	private static final float VALOR_REBOTE_PELOTA = 1.7f;
	public static final String CAMPO_SHAPE = "CAMPO_SHAPE";
	public static final String CHAPA_LOCAL_SHAPE = "CHAPA_LOCAL_SHAPE";
	public static final String CHAPA_VISITANTE_SHAPE = "CHAPA_VISITANTE_SHAPE";
	public static final String CHAPA_SHADOW_SHAPE = "CHAPA_SHADOW_SHAPE";
	public static final String BALON_SHAPE = "BALON_SHAPE";
	public static final String BALON_SHADOW_SHAPE = "BALON_SHADOW_SHAPE";
	public static final String BOTON_SIGUIENTE_SHAPE = "BOTON_SIGUIENTE_SHAPE";
	public static final String BOTON_ANTERIOR_SHAPE = "BOTON_ANTERIOR_SHAPE";
	public static final String HALO_SHAPE = "HALO_SHAPE";
	public static final String OPTIONS_BACKGROUND_SHAPE = "OPTIONS_BACKGROUND_SHAPE";
	public static final String OPTIONS_MINUS_SHAPE = "OPTIONS_MINUS_SHAPE";
	public static final String OPTIONS_PLUS_SHAPE = "OPTIONS_PLUS_SHAPE";
	public static final String OPTIONS_VALUE_SHAPE = "OPTIONS_VALUE_SHAPE";
	
	public static final String PRINCIPAL_BACKGROUND_SHAPE = "PRINCIPAL_BACKGROUND_SHAPE";
	public static final String BG_CARTEL_SHAPE = "BG_CARTEL_SHAPE";

	/*
	 * MENSAJES
	 */
	public static final String MENSAJE_GOL = "GOL";
	public static final String MENSAJE_KICKOFF = "KICKOFF";
	public static final String MENSAJE_FIN_PARTIDO = "FIN_PARTIDO";

	/*
	 * Chapa fantasma
	 */
	private boolean arrastrandoChapa = false;
	private Chapa chapaPulsada = null;

	public Text mGolText;

	private PhysicsWorld physicsWorld;

	private ChapasFutbolActivity activity;

	private Campo campo;

	private List<Chapa> chapas = new ArrayList<Chapa>();
	private List<Halo> halos = new ArrayList<Halo>();
//	private List<Halo> halosLocales = new ArrayList<Halo>();
//	private List<Halo> halosVisitantes = new ArrayList<Halo>();

	private Pelota pelota;

	/**
	 * Scene para el menú contextual.
	 */
	public MenuScene sceneMenuContextual;

	/*
	 * HUD
	 */
	private static Hud hud;
	private static HUD hudCamara;

	/*
	 * Opciones.
	 */
	Opciones opciones = new Opciones();

	private Text textoParaContinuar;
	
	private TiradorChapa tiradorChapa;

	/*
	 * Propiedad que mantedrá el estado del juego.
	 */
	private static StateGame estado = new StateGame();
	
	private Multiplayer multiplayer;

	private MenuScene mExitOnline;
	private MenuScene mExitGame;

	/*
	 * Control de cámara
	 */
	private ZoomHandler zoomHandler;
	private ScrollHandler scrollHandler;
	public CameraHandler cameraHandler;
	
	/*
	 * Control de la escena.
	 */
	private SceneHandler sceneHandler;

	private EndGameMenu mEndGame;

	public Game(ChapasFutbolActivity activity) {
		
		this.activity = activity;
		/*
		 * Establecemos los margenes sobre los que van a rebotar las chapas y la
		 * pelota.
		 */
		this.sceneHandler = new SceneHandler(this);

//		iniciaSpritesJuego(); //FIXME: pasar a los inicializadores de las escenas.

		this.cameraHandler = new CameraHandler(this);
		
		this.zoomHandler = new ZoomHandler(
				(ZoomCamera) this.activity.getEngine().getCamera(), activity);
		
		this.scrollHandler = new ScrollHandler(
				(ZoomCamera) this.activity.getEngine().getCamera());
	}

	/**
	 * Pétodo que coloca los sprites en el campo de juego.
	 */
	public void iniciaSpritesJuego() {
		this.physicsWorld = new FixedStepPhysicsWorld(30, new Vector2(0, 0), true, 3, 2);
		
		this.physicsWorld.setContactListener(new ContactListener() {
			
			public void preSolve(Contact contact, Manifold oldManifold) {
				// NADA	
			}
			
			public void postSolve(Contact contact, ContactImpulse impulse) {
			}
			
			public void endContact(Contact contact) {
				// NADA	
			}
			
			public void beginContact(Contact contact) {
				
				// Se podría hacer estableciendo datos de usuario en los bodys
				// ya que se pueden acceder desde los fixtures.
				if(!contact.getFixtureA().equals(goalFixtureDef) && 
				   !contact.getFixtureB().equals(goalFixtureDef) &&
				   Chapa.CHAPA_USER_DATA.equals(contact.getFixtureA().getBody().getUserData()) && 
				   Chapa.CHAPA_USER_DATA.equals(contact.getFixtureB().getBody().getUserData()))
				{
					Game.this.activity.getSoundHandler().playFx(Resources.SOUND_CHAPAS);
				}else{
					Game.this.activity.getSoundHandler().playFx(Resources.SOUND_CHUTE);
				}
			}
		});

		tiradorChapa = new TiradorChapa(this);
		hud = new Hud(this);
		hudCamara = new HUD();
		
		// FIXME: esta inicialización es temporal hasta que se vea que se queda
		// en el HUD.
		for (int i = 0; i < GameScene.LAYER_COUNT; i++) {
			hudCamara.attachChild(new Entity());
		}	
		
		iniciaBordes();
		
		/*
		 * Establecer todos los elementos de la escena.
		 */
		campo = new Campo(
				- activity.getResources().getInteger(R.integer.camera_margin_x), 
				- activity.getResources().getInteger(R.integer.camera_margin_y), 
				activity.getShapesMaps().get(CAMPO_SHAPE),
				activity.getVertexBufferObjectManager());
		campo.setWidth(activity.getResources().getInteger(R.integer.camera_margin_x) * 2 + activity.getResources().getInteger(R.integer.game_place_width));
		campo.setHeight(activity.getResources().getInteger(R.integer.camera_margin_y) * 2 + activity.getResources().getInteger(R.integer.game_place_height));
		sceneHandler.getGame().attachChild(GameScene.LAYER_CAMPO, campo);

		Sprite lineasCampo = new Sprite(activity.getResources().getInteger(R.integer.game_place_width)
				/ 2
				- Resources.graphicResources.get(Resources.ATLAS_LINEAS_CAMPO)
						.getResources().get(Resources.SHAPE_GROUND_LINES)
						.getWidth() / 2, activity.getResources().getInteger(R.integer.game_place_height)
				/ 2
				- Resources.graphicResources.get(Resources.ATLAS_LINEAS_CAMPO)
						.getResources().get(Resources.SHAPE_GROUND_LINES)
						.getHeight() / 2, activity.getShapesMaps().get(
				Resources.SHAPE_GROUND_LINES),
				activity.getVertexBufferObjectManager());
		sceneHandler.getGame().attachChild(GameScene.LAYER_CAMPO, lineasCampo);
		
		// Chapas Locales
		int count = 0;
		Chapa chapa = null;
		Sprite sombra = null;
		for (int punto : activity.getResources().getIntArray(R.array.chapPosIni)) {
			if(count%2 == 0){
				chapa = new ChapaLocal(punto - Chapa.WIDTH / 2, punto
						- Chapa.HEIGHT / 2, activity.getShapesMaps().get(
								Resources.SHAPE_CHAPA_LOCAL),
								activity.getVertexBufferObjectManager(), this,
								VALOR_REBOTE_CHAPA);
				sombra = new Sprite(chapa.getX() + activity.getResources().getInteger(R.integer.sombra_x_offset), 
						chapa.getY() + activity.getResources().getInteger(R.integer.sombra_y_offset),
						activity.getShapesMaps().get(Resources.SHAPE_SOMBRA_CHAPA), 
						activity.getVertexBufferObjectManager());
				chapa.setSombra(sombra);
			}else{
				chapa.setY(punto - Chapa.HEIGHT / 2);
				sombra.setY(chapa.getY() + activity.getResources().getInteger(R.integer.sombra_y_offset));
				chapas.add(chapa);
			}
			count++;
		}

		// Chapas Visitantes
		count = 0;
		for (int punto : activity.getResources().getIntArray(R.array.chapPosIni)){
			if(count%2 == 0){
				chapa = new ChapaVisitante(activity.getResources().getInteger(R.integer.game_place_width) - punto - Chapa.WIDTH
						/ 2, activity.getResources().getInteger(R.integer.game_place_height) - punto - Chapa.HEIGHT / 2, activity
						.getShapesMaps().get(Resources.SHAPE_CHAPA_VISITANTE), activity
						.getVertexBufferObjectManager(), this, VALOR_REBOTE_CHAPA); 
				sombra = new Sprite(chapa.getX() + activity.getResources().getInteger(R.integer.sombra_x_offset), 
						chapa.getY() + activity.getResources().getInteger(R.integer.sombra_y_offset),
						activity.getShapesMaps().get(Resources.SHAPE_SOMBRA_CHAPA), 
						activity.getVertexBufferObjectManager());
				chapa.setSombra(sombra);
			}else{
				chapa.setY(activity.getResources().getInteger(R.integer.game_place_height) - punto - Chapa.HEIGHT / 2);
				sombra.setY(chapa.getY() + activity.getResources().getInteger(R.integer.sombra_y_offset));
				chapas.add(chapa);
			}
			count++;
		}
		
		for (Chapa chapaIt : chapas) {
			sceneHandler.getGame().attachChild(GameScene.LAYER_CHAPAS, chapaIt);
			sceneHandler.getGame().attachChild(GameScene.LAYER_SHADOWS, chapaIt.getSombra());
			physicsWorld.registerPhysicsConnector(new PhysicsConnector(chapaIt,
					chapaIt.getBody(), true, true));
		}
		registrarChapasListeners(); //FIXME: A LA ESCENA.

		/*
		 * Ponemos la pelota en el campo.
		 */
		pelota = new Pelota(activity.getResources().getInteger(R.integer.game_place_width) / 2 - Pelota.WIDTH / 2, activity.getResources().getInteger(R.integer.game_place_height)
				/ 2 - Pelota.HEIGHT / 2, activity.getShapesMaps().get(
						Resources.SHAPE_BALON), activity.getVertexBufferObjectManager(), this,
				VALOR_REBOTE_PELOTA);
		Sprite sombraPelota = new Sprite(pelota.getX() + activity.getResources().getInteger(R.integer.sombra_x_offset), 
				pelota.getY() + activity.getResources().getInteger(R.integer.sombra_y_offset),
				activity.getShapesMaps().get(Resources.SHAPE_SOMBRA_BALON), 
				activity.getVertexBufferObjectManager());

		pelota.setSombra(sombraPelota);
		physicsWorld.registerPhysicsConnector(new PhysicsConnector(pelota,
				pelota.getBody(), true, true));

		sceneHandler.getGame().attachChild(GameScene.LAYER_PELOTA, pelota);
		sceneHandler.getGame().attachChild(GameScene.LAYER_SHADOWS, pelota.getSombra());
//		sceneHandler.getGame().attachChild(GameScene.LAYER_SCORE, hud.getmScoreText());//FIXME: AL HUD DE LA CAMARA.
		
//		hudCamara.getChildByIndex(GameScene.LAYER_SCORE).attachChild(hud.getmScoreText());
		
		Sprite s = hud.getBarraHud();
		s.attachChild(hud.getLogoLocalHud());
		s.attachChild(hud.getLogoVisitanteHud());
		s.attachChild(hud.getMarcadorHud());
		s.attachChild(hud.getTiempoHud());
		s.attachChild(hud.getmScoreText());
		s.attachChild(hud.getmRestText());
		s.attachChild(hud.getmLocalLabel());
		s.attachChild(hud.getmVisitLabel());
		
//		s.attachChild(hud.getBarraTiempo());
		
		hudCamara.getChildByIndex(GameScene.LAYER_SCORE).attachChild(s);
		
//		temporizadorBarrasDeTiempo();
		
//		hudCamara.getChildByIndex(GameScene.LAYER_SCORE).attachChild(hud.getLogoLocalHud());
//		hudCamara.getChildByIndex(GameScene.LAYER_SCORE).attachChild(hud.getLogoVisitanteHud());
//		hudCamara.getChildByIndex(GameScene.LAYER_SCORE).attachChild(hud.getMarcadorHud());
//		hudCamara.getChildByIndex(GameScene.LAYER_SCORE).attachChild(hud.getTiempoHud());
//		activity.mCamera.setHUD(hudCamara);

		/* Texto con movimiento para el gol. */
		mGolText = new Text(0, 0, 
				activity.getFontMap().get(Resources.FONT_DEFAULT), "Gol !!!", new TextOptions(
				HorizontalAlign.CENTER),
				activity.getVertexBufferObjectManager());
		mGolText.setPosition(
				(activity.getEngine().getCamera().getWidth() - mGolText.getWidth()) * 0.5f,
//				(activity.getEngine().getCamera().getWidth()- mGolText.getHeight()) * 0.5f);
				(activity.getEngine().getCamera().getHeight() - mGolText.getHeight()) * 0.5f);
		mGolText.registerEntityModifier(new ScaleModifier(3, 0.1f, 2.0f));
		// mGolText.registerEntityModifier(new RotationModifier(3, 0, 720));

		/*
		 * Porterías
		 */
		ITextureRegion goalShape = activity.getShapesMaps().get(Resources.GOAL_SHAPE);
		Sprite porteriaLeft = new Sprite(41 - 100, 
				343 - 100, 
				goalShape, activity.getVertexBufferObjectManager());
		
		Sprite porteriaRight = new Sprite(1355 - 100, 
				343 - 100, 
				goalShape, activity.getVertexBufferObjectManager());
		
		porteriaRight.setFlipped(true, false);
		
		sceneHandler.getGame().attachChild(GameScene.LAYER_PORTERIAS, porteriaLeft);
		sceneHandler.getGame().attachChild(GameScene.LAYER_PORTERIAS, porteriaRight);
		
		sceneHandler.getGame().registerUpdateHandler(new TimerHandler(1, true,
				new ITimerCallback() {
					public void onTimePassed(TimerHandler pTimerHandler) {
						// Escribimos el código para iniciar el temporizador
						if (estado.isJuegoActivo() && opciones.isModoTiempo()) {
							hud.decrementaTiempo();
							if (isFinJuego()) {
								Game.this.mostrarMensaje(MENSAJE_FIN_PARTIDO);
								finalizarJuego();
							}
						}
					}
				}));
		
		sceneHandler.getGame().registerUpdateHandler(this.getPhysicsWorld());
		sceneHandler.getGame().registerUpdateHandler(this);
		sceneHandler.getGame().setOnSceneTouchListener(this); //FIXME: PASAR A LA ESCENA
		hud.iniciarMarcadores();
	}

	//	private void inciaScenePausa(){
//		this.mPauseScene = new CameraScene(activity.mCamera);
//		/* Make the 'PAUSED'-label centered on the camera. */
//		final float centerX = (activity.CAMERA_WIDTH - 
//				activity.mPausedTextureRegion.getWidth()) / 2;
//		final float centerY = (activity.CAMERA_HEIGHT - 
//				activity.mPausedTextureRegion.getHeight()) / 2;
//		final Sprite pausedSprite = new Sprite(centerX, centerY, 
//				activity.mPausedTextureRegion, 
//				activity.getVertexBufferObjectManager());
//		this.mPauseScene.attachChild(pausedSprite);
//		/* Makes the paused Game look through. */
//		this.mPauseScene.setBackgroundEnabled(false);
//	}
	
	/**
	 * Registra un temporizador que sirve para mostrar el mensaje de GOL
	 */
	private void temporizadorMensaje(float time, final Callback<String> callback) {

		/* El handler elimina el mensaje de gol pasados 3 segundos. */
		sceneHandler.getGame().registerUpdateHandler(new TimerHandler(time,
				new ITimerCallback() {
					public void onTimePassed(final TimerHandler pTimerHandler) {
						sceneHandler.getOptions().unregisterUpdateHandler(pTimerHandler);
						ocultaMensajes();
						if(callback != null){
							callback.onCallback(null);
						}
					}
				}));
	}
	
	/**
	 * Registra un temporizador que sirve para mostrar el tiempo restante por 
	 * turno.
	 */
//	private void temporizadorBarrasDeTiempo() {
//
//		th = new TimerHandler(0.1f, true,
//				new ITimerCallback() {
//			public void onTimePassed(TimerHandler pTimerHandler) {
//				
//				hud.decrementaBarraTiempo();
//				if (isFinTiempoTurno()) {
//					hud.inicializarBarraTurno();
//					cambiaTurno();
//					reanudarJuego();
//					colocaBarraTurno();
//				}
//			}
//		});
//		sceneHandler.getGame().registerUpdateHandler(th);
//	}

	/**
	 * Crea los bordes del campo para que las chapas y las pelotas no se salgan.
	 */
	private void iniciaBordes() {
		final Rectangle ground = new Rectangle(0, activity.getResources().getInteger(R.integer.game_place_height) - 2,
				activity.getResources().getInteger(R.integer.game_place_width), 2, activity.getVertexBufferObjectManager());
		final Rectangle roof = new Rectangle(0, 0, activity.getResources().getInteger(R.integer.game_place_width), 2,
				activity.getVertexBufferObjectManager());
		final Rectangle leftUp = new Rectangle(0, 0, 2, 286, //FIXME: PONER EN ALGUNA CONSTANTE. VA EN FUNCION DE LAS LINEAS DEL CAMPO
				activity.getVertexBufferObjectManager());
		final Rectangle leftDown = new Rectangle(0, 487, //FIXME: PONER EN ALGUNA CONSTANTE. VA EN FUNCION DE LAS LINEAS DEL CAMPO 
				2, 
				activity.getResources().getInteger(R.integer.game_place_height) - 487,
				activity.getVertexBufferObjectManager());
		final Rectangle leftGoal = new Rectangle(0, 286, //FIXME: PONER EN ALGUNA CONSTANTE. VA EN FUNCION DE LAS LINEAS DEL CAMPO 
				2, 
				487 - 286,
				activity.getVertexBufferObjectManager());
		final Rectangle rightUp = new Rectangle(activity.getResources().getInteger(R.integer.game_place_width) - 2, 0, 2, //FIXME: PONER EN ALGUNA CONSTANTE. VA EN FUNCION DE LAS LINEAS DEL CAMPO
				286, activity.getVertexBufferObjectManager());

		final Rectangle rightDown = new Rectangle(activity.getResources().getInteger(R.integer.game_place_width) - 2, 487, 2, //FIXME: PONER EN ALGUNA CONSTANTE. VA EN FUNCION DE LAS LINEAS DEL CAMPO
				activity.getResources().getInteger(R.integer.game_place_height) - 487, activity.getVertexBufferObjectManager());

		final Rectangle rightGoal = new Rectangle(activity.getResources().getInteger(R.integer.game_place_width) - 2, 286, 2, //FIXME: PONER EN ALGUNA CONSTANTE. VA EN FUNCION DE LAS LINEAS DEL CAMPO
				487 - 286, activity.getVertexBufferObjectManager());

		PhysicsFactory.createBoxBody(this.physicsWorld, ground,
				BodyType.StaticBody, wallFixtureDef);
		PhysicsFactory.createBoxBody(this.physicsWorld, roof,
				BodyType.StaticBody, wallFixtureDef);
		PhysicsFactory.createBoxBody(this.physicsWorld, leftUp,
				BodyType.StaticBody, wallFixtureDef);
		PhysicsFactory.createBoxBody(this.physicsWorld, leftDown,
				BodyType.StaticBody, wallFixtureDef);
		PhysicsFactory.createBoxBody(this.physicsWorld, leftGoal,
				BodyType.StaticBody, goalFixtureDef);
		PhysicsFactory.createBoxBody(this.physicsWorld, rightUp,
				BodyType.StaticBody, wallFixtureDef);
		PhysicsFactory.createBoxBody(this.physicsWorld, rightDown,
				BodyType.StaticBody, wallFixtureDef);
		PhysicsFactory.createBoxBody(this.physicsWorld, rightGoal,
				BodyType.StaticBody, goalFixtureDef);
	}

	/**
	 * Indica si es el turno de los locales.
	 * 
	 * @return
	 */
	public boolean isTurnoLocal() {
		return turnoLocal;
	}

	/**
	 * Indica si es turno de los visitantes.
	 * 
	 * @return
	 */
	public boolean isTurnoVisitante() {
		return !turnoLocal;
	}

	/**
	 * Cambia de turno de juego.
	 */
	public void cambiaTurno() {
		hud.incrementaTurno();
		turnoLocal = !turnoLocal;
	}

	public PhysicsWorld getPhysicsWorld() {
		return physicsWorld;
	}

	/**
	 * Método que establece la posicón inicial de los recursos. Se llama despues
	 * de un gol, o al iniciar un partido nuevo.
	 */
	private void inicializaRecursos() {
		/*
		 * Chapas y Pelota.
		 */
		pelota.getBody().setTransform(
				(activity.getResources().getInteger(R.integer.game_place_width) / 2) / PIXEL_TO_METER_RATIO_DEFAULT,
				(activity.getResources().getInteger(R.integer.game_place_height) / 2) / PIXEL_TO_METER_RATIO_DEFAULT, 0);
		pelota.getBody().setLinearVelocity(new Vector2());
		pelota.getBody().setAngularVelocity(0);

		int indLocal = 0;
		int indVisitante = 0;
		int pX;
		int pY;
		for (Chapa chapa : chapas) {
			Point punto = null;
			if (chapa instanceof ChapaLocal) {
				int ind = indLocal++ * 2;
				pX = activity.getResources().getIntArray(R.array.chapPosIni)[ind];
				pY = activity.getResources().getIntArray(R.array.chapPosIni)[ind+1];
			} else {
				int ind = indVisitante++ * 2;
				pX = activity.getResources().getInteger(R.integer.game_place_width) - activity.getResources().getIntArray(R.array.chapPosIni)[ind];
				pY = activity.getResources().getInteger(R.integer.game_place_height) - activity.getResources().getIntArray(R.array.chapPosIni)[ind+1];
			}
			chapa.getBody().setTransform(pX / PIXEL_TO_METER_RATIO_DEFAULT,
					pY / PIXEL_TO_METER_RATIO_DEFAULT, 0);
			chapa.getBody().setLinearVelocity(new Vector2());
			chapa.getBody().setAngularVelocity(0);
			chapa.setX(pX-Chapa.WIDTH/2);
			chapa.setY(pY-Chapa.HEIGHT/2);
		}
	}
	
	/** 
	 * Método que posiciona un indicador de turno sobre el jugador local
	 * o el visitante.
	 * 
	 */
	public void colocaHalos(){
		
		halos = new ArrayList<Halo>(){

			/**
			 * 
			 */
			private static final long serialVersionUID = -6508404766074730541L;};
		
		/**
		 * Establecemos algunas propiedades para simular que el halo palpita.
		 */
		// FIXME: ver por qué debo instanciar esta propiedad cada vez.
		LoopEntityModifier entityModifierHalo =
				new LoopEntityModifier(
						new IEntityModifierListener() {
							public void onModifierStarted(
									final IModifier<IEntity> pModifier, 
									final IEntity pItem) {
							}
							public void onModifierFinished(
									final IModifier<IEntity> pEntityModifier, 
									final IEntity pEntity) {
							}
						},
						2, /* Número de iteraciones */
						new ILoopEntityModifierListener() {
							public void onLoopStarted(
									final LoopModifier<IEntity> pLoopModifier, 
									final int pLoop, final int pLoopCount) {
							}
							public void onLoopFinished(
									final LoopModifier<IEntity> pLoopModifier, 
									final int pLoop, final int pLoopCount) {
							}
						},
						new SequenceEntityModifier(
								new ScaleModifier(1.5f, 1, 1.5f),
								new ScaleModifier(1, 1.5f, 1)
						)
				);
		
		// Recorro las chapas para saber sus posiciones actuales
		for (Chapa chapa : chapas) {
			if (isTurnoLocal()){
				if (chapa instanceof ChapaLocal){
					halos.add(new Halo( 
							(int)(chapa.getBody().getPosition().x * PIXEL_TO_METER_RATIO_DEFAULT - Halo.WIDTH/2 +1), 
							(int)(chapa.getBody().getPosition().y * PIXEL_TO_METER_RATIO_DEFAULT - Halo.HEIGHT/2 +1), 
							activity.getShapesMaps().get(HALO_SHAPE),
							activity.getVertexBufferObjectManager()));
				}
			}else{
				if (chapa instanceof ChapaVisitante){
					halos.add(new Halo( 
							(int)(chapa.getBody().getPosition().x * PIXEL_TO_METER_RATIO_DEFAULT - Halo.WIDTH/2 +1), 
							(int)(chapa.getBody().getPosition().y * PIXEL_TO_METER_RATIO_DEFAULT - Halo.HEIGHT/2 +1), 
							activity.getShapesMaps().get(HALO_SHAPE),
							activity.getVertexBufferObjectManager()));
				}
			}
		}
			
		for (Halo h : halos) {
			h.setBlendFunction(GLES20.GL_SRC_ALPHA, 
					GLES20.GL_ONE_MINUS_SRC_ALPHA);
			h.registerEntityModifier(entityModifierHalo);
			h.setAlpha(0.7f);
			sceneHandler.getGame().attachChild(GameScene.LAYER_HALOS, h);
		}
	}
	
	/**
	 * Método que implementa el comportamiento de gol en portería local.
	 */
	public void golLocal() {
		if (!opciones.isMultiplayer() || 
				(multiplayer.isLocal() && isTurnoVisitante()) || //Se comprueban los turnos al reves, ya que se cambia de turno al soltar la chapa. 
				(multiplayer.isVisitante() && isTurnoLocal())) {
			if(opciones.isMultiplayer()){
				ServerUtilities.callGoal(multiplayer.getOrderMessage(), multiplayer.getIdMatch(), 
						multiplayer.getIdClient(), 
						IMessage.LOCAL_TEAM, 
						new Callback<Exception>() {
					
					public void onCallback(Exception pCallbackValue) {
						onMultiplayerError(pCallbackValue);
					}
				});
			}
			turnoLocal = false;
			if (estado.isJuegoActivo()) {
				hud.incrementaMarcadorLocal();
			}
			gol();
		}
	}

	/**
	 * Método que implementa el comportamiento de gol en portería local.
	 */
	public void golVisitante() {
		if (!opciones.isMultiplayer() || 
				(multiplayer.isLocal() && isTurnoVisitante()) || //Se comprueban los turnos al reves, ya que se cambia de turno al soltar la chapa. 
				(multiplayer.isVisitante() && isTurnoLocal())) {
			if(opciones.isMultiplayer()){
				ServerUtilities.callGoal(multiplayer.getOrderMessage(), multiplayer.getIdMatch(), 
						multiplayer.getIdClient(), 
						IMessage.VISIT_TEAM, new Callback<Exception>() {
							public void onCallback(Exception pCallbackValue) {
								onMultiplayerError(pCallbackValue);
							}
						});
			}
			turnoLocal = true;
			if (estado.isJuegoActivo()) {
				hud.incrementaMarcadorVisitante();
			}
			gol();
		}
	}
	
	/**
	 * Se recibe un gol de forma remota.
	 */
	public void onRemoteGoal(String team) {
		if(team.equals(IMessage.LOCAL_TEAM)){
			turnoLocal = false;
			if (estado.isJuegoActivo()) {
				hud.incrementaMarcadorLocal();
			}
		}else{
			turnoLocal = true;
			if (estado.isJuegoActivo()) {
				hud.incrementaMarcadorVisitante();
			}
		}
		gol();
	}
	
	public void gol(){
		estado.pararJuego();
		if (opciones.isModoGoles() && isFinJuego()) {
			this.mostrarMensaje(MENSAJE_FIN_PARTIDO);
			finalizarJuego();
		} else {
			mostrarMensaje(MENSAJE_GOL);
		}
	}

	/**
	 * Método que finaliza el juego. Establecer el estado de finalización.
	 * TODO:Mostrar mensaje de finalizado. Debe de cargar la pantalla
	 * correspondiente al resultado
	 */
	public void finalizarJuego() {
		if (opciones.isMultiplayer()) {
			ServerUtilities.callEndMatch(multiplayer.getOrderMessage(), multiplayer.getIdMatch());
			multiplayer.resetAndStop();
		}
		estado.finalizaJuego();
		Debug.d("Fin del partido. Equipo ganador:" + getEquipoGanador());
//		this.eliminarAreaListeners();
		this.clearGameTouchAreas();
		this.clearHudTouchAreas();
	}

//	private void iniciarEscenaBluetooth() {
//		Sprite bBack = new Sprite(941, 635, activity.getShapesMaps().get(
//				BOTON_SIGUIENTE_SHAPE),
//				activity.getVertexBufferObjectManager()) {
//			@Override
//			public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
//					float pTouchAreaLocalX, float pTouchAreaLocalY) {
//				if (pSceneTouchEvent.isActionUp()) {
//					activity.getSoundHandler().playFx(Resources.SOUND_CLICK);
//					final Intent intent = new Intent(Game.this.getActivity(), BluetoothListDevicesActivity.class);
//					Game.this.getActivity().startActivityForResult(intent, MultiplayerBluetooth.REQUESTCODE_BLUETOOTH_CONNECT);
//				}
//				return true;
//			}
//		};
//		
//		sceneHandler.getBluetooth().setBackground(new SpriteBackground(bgCommon));
//		sceneHandler.getBluetooth().attachChild(bBack);
//		sceneHandler.getBluetooth().registerTouchArea(bBack);
//	}
//

	/**
	 * Método que calcula el equipo ganador. Puiendo darse el caso de existir
	 * empate.
	 */
	public String getEquipoGanador() {
		if (hud.getMarcadorLocal() == hud.getMarcadorVisitante()) {
			return (String) activity.getText(R.string.tWinDraw);
		} else if (hud.getMarcadorLocal() < hud.getMarcadorVisitante()) {
			return (String) activity.getText(R.string.tWinVisitant);
		}
		return (String) activity.getText(R.string.tWinLocal);
	}

	/**
	 * Detecta si ha terminado el juego En el caso de que esté en modo gol, que
	 * la suma de los goles locales + visitantes sea el máximo de goles
	 * establecido. En el caso de que el modo de juego sea por turnos, que se
	 * hayan consumido todos los turnos. En el caso de que sea en modo Tiempo,
	 * que se haya consumido todo el tiempo de juego.
	 * 
	 * @return
	 */
	private boolean isFinJuego() {
		if (opciones.isModoGoles()) {
			return (hud.getMarcadorLocal() + hud.getMarcadorVisitante()) >= opciones
					.getGolesMax();
		} else if (opciones.isModoTurnos()) {
			return hud.getTurnos() >= opciones.getTurnos();
		} else if (opciones.isModoTiempo()) {
			return hud.getTiempo() <= 0;
		}
		return false;
	}

//	private boolean isFinTiempoTurno() {
//		return hud.getTiempoTurno() <= 0;
//	}

	/**
	 * Método que permite mostrar un mensaje por pantalla. Generalmente para
	 * informar de que se ha marcado un gol. TODO:
	 * 
	 * @param mensaje
	 */
	private void mostrarMensaje(String mensaje) {
		if (mensaje.equals(MENSAJE_GOL)) {
			activity.getSoundHandler().playFx(Resources.SOUND_GOL);
			Sprite bgGol = new Sprite(0, 0, 
					activity.getShapesMaps().get(Resources.BG_GOAL_SHAPE), 
					activity.getVertexBufferObjectManager());
			
			// FIXME: ver por qué debo instanciar esta propiedad cada vez.
			LoopEntityModifier entityModifierFondo =
					new LoopEntityModifier(
							new IEntityModifierListener() {
								public void onModifierStarted(
										final IModifier<IEntity> pModifier, 
										final IEntity pItem) {
								}
								public void onModifierFinished(
										final IModifier<IEntity> pEntityModifier, 
										final IEntity pEntity) {
								}
							},
							1, /* Número de iteraciones */
							new ILoopEntityModifierListener() {
								public void onLoopStarted(
										final LoopModifier<IEntity> pLoopModifier, 
										final int pLoop, final int pLoopCount) {
								}
								public void onLoopFinished(
										final LoopModifier<IEntity> pLoopModifier, 
										final int pLoop, final int pLoopCount) {
								}
							},
							new SequenceEntityModifier(
									new ScaleModifier(0.2f, 1, 1.5f),
									new ScaleModifier(0.5f, 1.5f, 1),
									new ScaleModifier(0.2f, 1, 1.5f),
									new ScaleModifier(0.5f, 1.5f, 1)
							)
					);
			
			bgGol.registerEntityModifier(entityModifierFondo);
			hudCamara.getChildByIndex(GameScene.LAYER_GOL_TEXT).attachChild(bgGol);
			
			Sprite monigoteGol = new Sprite(10, 10, 
					activity.getShapesMaps().get(Resources.MONIGOTE_GOAL_SHAPE), 
					activity.getVertexBufferObjectManager());
			
			// FIXME: ver por qué debo instanciar esta propiedad cada vez.
			LoopEntityModifier entityModifierMonigote =
					new LoopEntityModifier(
							new IEntityModifierListener() {
								public void onModifierStarted(
										final IModifier<IEntity> pModifier, 
										final IEntity pItem) {
								}
								public void onModifierFinished(
										final IModifier<IEntity> pEntityModifier, 
										final IEntity pEntity) {
								}
							},
							1, /* Número de iteraciones */
							new ILoopEntityModifierListener() {
								public void onLoopStarted(
										final LoopModifier<IEntity> pLoopModifier, 
										final int pLoop, final int pLoopCount) {
								}
								public void onLoopFinished(
										final LoopModifier<IEntity> pLoopModifier, 
										final int pLoop, final int pLoopCount) {
								}
							},
							new SequenceEntityModifier(
									new MoveModifier(0.8f, 
											activity.getEngine().getCamera().getWidth(), 
											200, 0, 0)
							)
					);
			
			
			monigoteGol.registerEntityModifier(entityModifierMonigote);
			hudCamara.getChildByIndex(GameScene.LAYER_GOL_TEXT).attachChild(monigoteGol);
			
//			getTextoMensaje(R.string.tMessageGoal, activity.getFontMap().get(Resources.FONT_DEFAULT), 3f); 
			
		} else if(mensaje.equals(MENSAJE_FIN_PARTIDO)){
			activity.getSoundHandler().playFx(Resources.SOUND_SILBATO_FIN);
			getTextoMensaje(R.string.tMessageEndMatch, activity.getFontMap().get(Resources.FONT_DEFAULT), 3f);  
		} else if(mensaje.equals(MENSAJE_KICKOFF)){
			activity.getSoundHandler().playFx(Resources.SOUND_SILBATO);
			getTextoMensaje(R.string.tMessageKickOff, activity.getFontMap().get(Resources.FONT_DEFAULT), 3f);
		}else{
			Debug.d("Mensaje no definido!!");
		}
	}
	
	private void getTextoMensaje(int idMensaje, Font font, float time){
		getTextoMensaje(idMensaje, font, time, null);
	}
	
	private void getTextoMensaje(int idMensaje, Font font, float time, Callback<String> callback){
		hudCamara.getChildByIndex(GameScene.LAYER_GOL_TEXT).attachChild(hud.getMessage(idMensaje));
		temporizadorMensaje(time, callback);
	}

	/**
	 * Método que permite reanudar el juego. Generalmente despues de parar el
	 * juego por un gol u otro evento. Coloca los recursos (chapas y pelota) en
	 * su posición inicial Establece el estado de Activo Registra los listeners
	 * de las chapas.
	 */
	public void reanudarJuego() {
		cameraHandler.resetCamera();
		ocultaMensajes(); // Para el caso en que el temporizador no lo haya hecho.
		tiradorChapa.ocultaVectorFuerza(); // FIXME: habrá que fusionar el código del vector fuerza y el tirador.
		tiradorChapa.ocultarTirador();
		eliminaHalos();
		inicializaRecursos();
		estado.reiniciarEstado();
		registrarChapasListeners();
		colocaHalos();
		muestraHud();
	}
	
	public void muestraHud(){
		activity.mCamera.setHUD(hudCamara);
	}

	public ChapasFutbolActivity getActivity() {
		return activity;
	}

	/**
	 * Elimina los listeners de las chapas para que no de errores a la hora de
	 * arrastrar el tirador sobre otra chapa.
	 *  NO UTILIZAR. Usar en su caso <code>clearHudTouchAreas</code> o <code>clearGameTouchAreas</code>  
	 */
	@Deprecated
	public void eliminarAreaListeners() {
		hudCamara.clearTouchAreas();
		sceneHandler.getGame().clearTouchAreas();
	}

	public void clearGameTouchAreas(){
		sceneHandler.getGame().clearTouchAreas();
	}
	
	/**
	 * Elimina las areas sensibles del hud
	 */
	public void clearHudTouchAreas(){
		hudCamara.clearTouchAreas();
	}

	/**
	 * Este método vuelve a registrar los listeners de las chapas. TODO: ES
	 * POSIBLE QUE SE ESTÉN ACTIVANDO ANTES DE TIEMPO YA QUE EXISTE UN
	 * COMPORTAMIENTO ERRÁTICO A LA HORA DE ARRASTRAR UNA CHAPA SOBRE OTRA.
	 */
	private void registrarChapasListeners() {
		if (!opciones.isMultiplayer()
				|| ((multiplayer.isLocal() && isTurnoLocal()) || (multiplayer
						.isVisitante() && isTurnoVisitante()))) {
			/*
			 * Solo se registrarán los listeners en el caso de que sea partida
			 * local, o sea nuestro turno en multiplayer.
			 */
			clearGameTouchAreas();
			for (Chapa chapa : chapas) {
				sceneHandler.getGame().registerTouchArea(chapa);
			}
		}
	}

	/**
	 * Método que nueve el tirador creado cuando se pulsa y arrastra una
	 * chapa.
	 * 
	 * @param pSceneTouchEvent
	 */
	public void mueveTirador(TouchEvent pSceneTouchEvent) {
		if(tiradorChapa.isEstadoTirador()){
			tiradorChapa.getTirador(pSceneTouchEvent.getX(),
					pSceneTouchEvent.getY());
		}
	}

	/**
	 * Crea el tirador que será el que se mueva segun se va arrastrando.
	 * Crea el tirador en la escena. 
	 * Informa de que se está arrastrando la chapa. 
	 * Elimina los listeners de las chapas para que no se pueda volver a pulsar una chapa.
	 * Registrar el tirador como area listener.
	 * 
	 * @param pSceneTouchEvent
	 */
	public void chapaPulsada(Chapa chapa, TouchEvent pSceneTouchEvent) {
		chapaPulsada = chapa;
		sceneHandler.getGame().attachChild(GameScene.LAYER_TIRADOR_CHAPA,
				tiradorChapa.getTirador(pSceneTouchEvent.getX(), pSceneTouchEvent.getY(), chapa));
		sceneHandler.getGame().attachChild(GameScene.LAYER_VECTOR_FUERZA,
				tiradorChapa.getVectorFuerza(pSceneTouchEvent.getX(), pSceneTouchEvent.getY(), chapa));
		sceneHandler.getGame().attachChild(GameScene.LAYER_CIRCULO_TIRADOR, tiradorChapa.getCirculo());
		this.arrastrandoChapa = true;
		registrarListenerTirador();
	}
	
	public void cancelaChapaPulsada(){
		chapaPulsada = null;
		arrastrandoChapa = false;
		registrarChapasListeners();
	}

	/**
	 * Borra la chapa fantasma Elimina de la escena la chapa de forma segura,
	 * obteniendo un bloqueo del engine. TODO: REQUIERE CAMBIARLE EL NOMBRE, YA
	 * QUE ACTUALMENTE ESTÁ BORRANDO LA CHAPA FANTASMA Y EL VECTOR FUERZA.
	 * POSTERIORMENTE BORRARÁ TODO LO REFERENTE AL TIRADOR DE LA CHAPA.
	 */
	public void borraTirador() {
			final EngineLock engineLock = activity.getEngine().getEngineLock();
			engineLock.lock();

			/* Now it is save to remove the entity! */
			sceneHandler.getGame().getChildByIndex(GameScene.LAYER_TIRADOR_CHAPA).detachChildren();
			sceneHandler.getGame().getChildByIndex(GameScene.LAYER_VECTOR_FUERZA).detachChildren();
			
			// No eliminamos el tirador puesto que se van a utilizar constantemente durante la partida.
			// Tan solo lo sacamos de su capa. 
			engineLock.unlock();
	}

	/**
	 * Indica si se está arrastrando una chapa actualmente. Paso previo para
	 * lanzar una chapa.
	 * 
	 * @return
	 */
	public boolean isArrastrandoChapa() {
		return arrastrandoChapa;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.andengine.engine.handler.IUpdateHandler#onUpdate(float)
	 * 
	 * Metodo que se llama cada vez que se actualiza la escena del juego.
	 * 
	 * Actualmente se comprueba: Existen chapas en movimiento. Ha finalizado el
	 * juego en el caso de ser juego por turnos.
	 */
	public void onUpdate(float pSecondsElapsed) {
		if (estado.isEnMovimiento()) {
			boolean moviendose = false;
			for (Chapa chapa : chapas) {
				if (chapa.getBody().isAwake()) {
					moviendose = true;
					break;
				}
			}
			moviendose = moviendose || pelota.getBody().isAwake();
			if (!moviendose) {
				if (opciones.isMultiplayer()) {
					//Esto tiene un tema chulo, ya que el turno se 
					//cambia cuando se lanza la chapa, por lo tanto, 
					//cuando estas se paran, es el turno contrario.
					if ((multiplayer.isLocal() && !isTurnoLocal())
							|| (multiplayer.isVisitante() && !isTurnoVisitante())) {
						enableGame();
						sendPosToSrv();
					}
				}else{
					enableGame();
				}
			}
		} else if (estado.isJuegoActivo() && opciones.isModoTurnos()) {
			if (isFinJuego()) {
				finalizarJuego();
				this.mostrarMensaje(MENSAJE_FIN_PARTIDO);
			}
		}
	}

	/**
	 * Metodo que habilita el juego, una vez que se paran las chapas.
	 */
	private void enableGame() {
		registrarChapasListeners();
		estado.activarJuego();
		
		// Muestro los halos solo cuando las chapas estén paradas.
		colocaHalos();
//		colocaBarraTurno();
		cameraHandler.chaseEntity(null);
	}

	/**
	 * Método que envia la posición final de las chapas y la pelota al servidor
	 * en el caso del multiplayer.
	 */
	private void sendPosToSrv() {
		long idClient;
		long idMatch;
		List<Float> capsPos;
		float ballX;
		float ballY;
		
		idClient = multiplayer.getIdClient();
		idMatch = multiplayer.getIdMatch();
		
		capsPos = new ArrayList<Float>();
		for(Chapa chapa : chapas){
			capsPos.add(chapa.getX());
			capsPos.add(chapa.getY());
		}
		
		ballX = pelota.getX();
		ballY = pelota.getY();
		
		try {
			ServerUtilities.callUpdatePositions(multiplayer.getOrderMessage(), idClient, 
					idMatch, 
					capsPos, 
					ballX, 
					ballY, 
					new Callback<Exception>() {
						public void onCallback(Exception pCallbackValue) {
							onMultiplayerError(pCallbackValue);
						}
					});
		} catch (IOException e) {
			networkError(e);
		} catch (JSONException e) {
			networkError(e);
		}
	}

	//FIXME: POR IMPLEMENTAR
	/*
	 * Método que debe de mostar un cartel informativo del error y salir del partido.
	 */
	private void networkError(Exception e) {
		
	}

	/**
	 * Implementación del updatehandler Se pone una traza para aprender más
	 * sobre cuando se llama este evento.
	 */
	public void reset() {
		Debug.d("Se llama al evento RESET");
	}

	/**
	 * Metodo que reinicia los marcadores y contadores del juego y vuelve a
	 * colocar las piezas en su sitio para comenzar otro partido.
	 */
	public void reiniciarJuego() {
		turnoLocal = true;
		hud.iniciarMarcadores();
		reanudarJuego();
	}

	public Scene getSceneMenuContextual() {
		return sceneMenuContextual;
	}

	/**
	 * Este método es opcional, Lo implementaremos en el caso de que queramos
	 * mostrar solo una parte del campo y no el campo completo ya que será
	 * necesario poder arrastrar por el campo para ver el campo completo o para
	 * hacer zoom.
	 */
	public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
		if(tiradorChapa.isEventoTirador()){
			tiradorChapa.onAreaTouched(pSceneTouchEvent, pSceneTouchEvent.getX(), pSceneTouchEvent.getX());
		}else{
			this.zoomHandler.getPinchZoomDetector().onTouchEvent(pSceneTouchEvent);
			
			if(this.zoomHandler.getPinchZoomDetector().isZooming()) {
				this.scrollHandler.getScrollDetector().setEnabled(false);
			} else {
				if(pSceneTouchEvent.isActionDown()) {
					this.scrollHandler.getScrollDetector().setEnabled(true);
				}else if (pSceneTouchEvent.isActionUp()) {
					if (estado.isJuegoParado()) {
						// Juego inactivo.
						if (opciones.isMultiplayer()) {
							try {
								ServerUtilities.callResumeMatch(multiplayer.getOrderMessage(), multiplayer.getIdClient(), 
										multiplayer.getIdMatch());
							} catch (JSONException e) {
								e.printStackTrace();
							} catch (IOException e) {
								e.printStackTrace();
							}
						}
						this.reanudarJuego();
					} else if (estado.isJuegoFinalizado()) {
						this.loadSummaryScene();
					}
					
				}
				this.scrollHandler.getScrollDetector().onTouchEvent(pSceneTouchEvent);
				
				/*
				 * Comprobamos si se esta reseteando el zoom.
				 * DESACTIVAMOS EL EVENTO DE RESET DE LA CÁMARA POR QUE PARECE SER ALGO QUE
				 * NO TERMINA DE IR FINO.
				 */
//				if(cameraHandler.isResetEvent(pSceneTouchEvent)){
//					cameraHandler.resetCamera();
//				}
			}
		}
		
		/*
		 * Ocultamos o mostramos el HUD segun sea el caso.
		 */
		if(pSceneTouchEvent.isActionUp()){
			hud.showHud();
		}else if(pSceneTouchEvent.isActionDown()){
			hud.hideHud();
		}
		
		return true;
	}
	
	/**
	 * Método que muestra los botones del tirador.
	 * Botones que permitirán aceptar o cancelar un tiro.
	 */
	public void mostrarBotonesTirador() {
		tiradorChapa.ocultarBotones();
		
		hudCamara.getChildByIndex(GameScene.LAYER_TIRADOR_BOTONES).attachChild(tiradorChapa.getBotonOk());
		hudCamara.getChildByIndex(GameScene.LAYER_TIRADOR_BOTONES).attachChild(tiradorChapa.getBotonCancel());
		hudCamara.registerTouchArea(tiradorChapa.getBotonOk());
		hudCamara.registerTouchArea(tiradorChapa.getBotonCancel());
	}

	/**
	 * Método que elimina los sprites de halos.
	 */
	public void eliminaHalos(){
		
		final EngineLock engineLock = activity.getEngine().getEngineLock();
		engineLock.lock();
		
		for (Halo h : halos) {
			sceneHandler.getGame().getChildByIndex(GameScene.LAYER_HALOS).detachChild(h);
		}
		
		engineLock.unlock();
	}
	
	/**
	 * Método que elimina el hud.
	 */
	public void eliminaHud(){
		activity.mCamera.setHUD(null);
	}

	/**
	 * Método que registra los listeners del tirador.
	 */
	private void registrarListenerTirador() {
		clearGameTouchAreas();
		sceneHandler.getGame().registerTouchArea(tiradorChapa.getTirador());
	}

	public StateGame getEstado() {
		return estado;
	}

	public Opciones getOpciones() {
		return opciones;
	}
	
	/**
	 * Método que oculta de la pantalla el mensaje de gol, en caso de que el
	 * temporizador no lo haya hecho.
	 */
	private void ocultaMensajes(){
		final EngineLock engineLock = activity.getEngine().getEngineLock();
		engineLock.lock();
		hudCamara.getChildByIndex(GameScene.LAYER_GOL_TEXT).detachChildren();
		engineLock.unlock();
	}

	/**
	 * Método que acepta el disparo desde el tirador.
	 * @param i
	 * @param j
	 */
	public void aceptarDisparo(float x, float y) {
		Debug.d("Fuerza disparo X: " + x + " Y: " + y);
		
		/*
		 * Importante, enviar primero al multiplayer por 
		 * que así se reducirá (poco) la demora de la respuesta.
		 */
		if (opciones.isMultiplayer()) {
			try {
				ServerUtilities.callShoot(multiplayer.getOrderMessage(), multiplayer.getIdClient(), 
					multiplayer.getIdMatch(), 
					chapas.indexOf(chapaPulsada), x, y,new Callback<Exception>() {
					public void onCallback(Exception pCallbackValue) {
						onMultiplayerError(pCallbackValue);
					}
				});
			} catch (IOException e) {
				e.printStackTrace();
			} catch (JSONException e) {
				e.printStackTrace();
			}
		}
		
		moverChapa(chapas.indexOf(chapaPulsada), x, y);
		borraTirador(); //TODO ESTO DEBE DE IR FUERA YA QUE EL TIRADOR SE BORRA EL SOLO. SE DEJA PARA BORRAR EL VECTOR FUERZA.
		arrastrandoChapa = false;
		chapaPulsada = null;
		cameraHandler.chaseEntity(pelota);
		
//		temporizadorBarrasDeTiempo();
	}

	public void moverChapa(int idChapa, float xPos, float yPos) {
		cambiaTurno();
		chapas.get(idChapa).mover(xPos, yPos);
		estado.ponerEnMovimiento();
		eliminaHalos();
	}	

	/**
	 * Método que carga la partida.
	 */
	public void loadGameScene() {
		
		// Paramos la música principal cuando empieza el partido.
		if (activity.getSoundHandler().isMusicPlaying()){
			activity.getSoundHandler().pauseMusic();
		}
		
		reiniciarJuego();
		sceneHandler.setGame();
		mostrarMensaje(MENSAJE_KICKOFF);
		
		activity.getSoundHandler().playMusic(Resources.MUSIC_CROWD, true);
	}

	/**
	 * Carga la escena de créditos.
	 */
	public void loadCreditsScene() {
		cameraHandler.resetCameraMenu();
		
		if(activity.getSoundHandler().isMusicLabelPlaying(Resources.MUSIC_PRINCIPAL)){
			activity.getSoundHandler().pauseMusicLabel(Resources.MUSIC_PRINCIPAL);
		}
		
		activity.getSoundHandler().playMusic(Resources.MUSIC_CREDITS, true);
		sceneHandler.setCredits();
	}

	/**
	 * Carga la escena de resumen
	 */
	public void loadSummaryScene(){
		cameraHandler.resetCameraMenu();
		this.eliminaHud();
		
		if(activity.getSoundHandler().isMusicLabelPlaying(Resources.MUSIC_CROWD)){
			activity.getSoundHandler().pauseMusicLabel(Resources.MUSIC_CROWD);
		}
		
		activity.getSoundHandler().playMusic(Resources.MUSIC_SUMMARY, true);
		
		sceneHandler.setSummary();
	}
	
	/**
	 * Método que permite cargar la escena de las opciones.
	 */
	public void loadOptionScene() {
		cameraHandler.resetCameraMenu();
		
		if(activity.getSoundHandler().isMusicLabelPlaying(Resources.MUSIC_CREDITS)){
			activity.getSoundHandler().pauseMusicLabel(Resources.MUSIC_CREDITS);
		}
		
		activity.getSoundHandler().playMusic(Resources.MUSIC_PRINCIPAL, true);
		sceneHandler.setOptionScene();
	}
	
	/**
	 * Método que permite cargar la escena del menú principal.
	 */
	public void loadMenuPrincipalScene() {
		cameraHandler.resetCameraMenu();
		
		if(activity.getSoundHandler().isMusicLabelPlaying(Resources.MUSIC_SUMMARY)){
			activity.getSoundHandler().stopMusicLabel(Resources.MUSIC_SUMMARY);
		}
		
		activity.getSoundHandler().playMusic(Resources.MUSIC_PRINCIPAL, true);
		
		sceneHandler.setMenuPrincipal();
	}

	/**
	 * Permite cargar la escena de espera al multiplayer.
	 */
	public void loadMultiplayerMatchScene() {
		sceneHandler.setWaitMultiplayer();
		if (multiplayer == null) {
			multiplayer = new Multiplayer(this);
		}
		new Thread(new Runnable() {
			public void run() {
				BaseMessage message = null;
				try {
					message = ServerUtilities.callLogin();
				} catch (final JSONException e) {
					e.printStackTrace();
					activity.runOnUpdateThread(new Runnable() {
						
						public void run() {
							onMultiplayerError(e);
							
						}
					});
				} catch (final IOException e) {
					e.printStackTrace();
					activity.runOnUpdateThread(new Runnable() {
						
						public void run() {
							onMultiplayerError(e);
							
						}
					});
				}
				if(message != null)
				if(message instanceof AckLoginMessage){
					AckLoginMessage loginMessage = (AckLoginMessage) message;
					multiplayer.setIdClient(loginMessage.getIdClient());
					/*
					 * Registrar un temporizador.
					 */
					activity.runOnUpdateThread(new Runnable() {
						
						public void run() {
							sceneHandler.getWaitMultiplayer().registerUpdateHandler(new TimerHandler(
									activity.getResources().getInteger(R.integer.multiplayer_timeout), 
									new ITimerCallback() {
										
										public void onTimePassed(TimerHandler pTimerHandler) {
											sceneHandler.getWaitMultiplayer().unregisterUpdateHandler(pTimerHandler);
											/*
											 * TIMEOUT
											 */
											try {
												ServerUtilities.callLeaveQueue(multiplayer.getIdClient());
											} catch (IOException e) {
												// TODO Auto-generated catch block
												e.printStackTrace();
											} catch (JSONException e) {
												// TODO Auto-generated catch block
												e.printStackTrace();
											}
											Game.this.timeOutMultiplayer();
										}
									}));
						}
					});

				}else{ //Debe de ser el mensaje de partido.
					MatchMessage matchMessage = (MatchMessage) message;
					multiplayer.setIdClient(matchMessage.getIdClient());
					multiplayer.setIdMatch(matchMessage.getIdMatch());
					multiplayer.setTeam(matchMessage.getTeam());
					multiplayer.setOrderRecived(1);
					activity.runOnUpdateThread(new Runnable() {
						public void run() {
							beforeMultiplayerMatch();
							
						}
					});
				}
			}
		}).start();
		multiplayer.startClient();
	}

	/**
	 * Carga en la pantalla del waiting multiplayer 
	 * información para que el usuario sepa que 
	 * ha pasado el tiempo de espera, 
	 * buscando contrincante.
	 */
	protected void timeOutMultiplayer() {
		getCartel(
				activity.getText(R.string.tMultiPlayerTimeOut), 
				activity.getText(R.string.tMultiPlayerRetry), 
				activity.getText(R.string.tMultiPlayerCancel), 
				new CartelAction() {
			public void onAccion() {
				Debug.d("Se reintenta la conexión.");
				BaseMessage message = null;
				try {
					message = ServerUtilities.callLogin();
					if(message instanceof AckLoginMessage){
						AckLoginMessage loginMessage = (AckLoginMessage) message;
						multiplayer.setIdClient(loginMessage.getIdClient());
						sceneHandler.getWaitMultiplayer().registerUpdateHandler(new TimerHandler(
								activity.getResources().getInteger(R.integer.multiplayer_timeout), 
								new ITimerCallback() {
									
									public void onTimePassed(TimerHandler pTimerHandler) {
										sceneHandler.getWaitMultiplayer().unregisterUpdateHandler(pTimerHandler);
										/*
										 * TIMEOUT
										 */
										try {
											ServerUtilities.callLeaveQueue(multiplayer.getIdClient());
										} catch (IOException e) {
											// TODO Auto-generated catch block
											e.printStackTrace();
										} catch (JSONException e) {
											// TODO Auto-generated catch block
											e.printStackTrace();
										}
//									multiplayer.abandonarCola();
										Game.this.timeOutMultiplayer();
									}
								}));
					}else{ //Debe de ser el mensaje de partido.
						MatchMessage matchMessage = (MatchMessage) message;
						multiplayer.setIdClient(matchMessage.getIdClient());
						multiplayer.setIdMatch(matchMessage.getIdMatch());
						multiplayer.setTeam(matchMessage.getTeam());
						multiplayer.setOrderRecived(1);
						beforeMultiplayerMatch();
					}
					
				} catch (JSONException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			public void onCancel() {
				Debug.d("Se cancela la conexión.");
				multiplayer.resetAndStop();
				Game.this.loadMenuPrincipalScene();
			}
		}, 
		sceneHandler.getWaitMultiplayer());
	}

	private Sprite getCartel(final CharSequence tCartel, 
			final CharSequence tBAction, 
			final CharSequence tBCancel, 
			final CartelAction cartelAction, 
			final Scene scena) {
		
		ITextureRegion bg = activity.getShapesMaps().get(BG_CARTEL_SHAPE);
		ITextureRegion btOk = activity.getShapesMaps().get(BOTON_SIGUIENTE_SHAPE);
		ITextureRegion btCancel = activity.getShapesMaps().get(BOTON_ANTERIOR_SHAPE);
		
		float bgCenX = bg.getWidth()/2;
		float bgCenY = bg.getHeight()/2;
		float gtOkCenX = btOk.getWidth()/2;
		float gtOkCenY = btOk.getHeight()/2;
		float gtCancelCenX = btCancel.getWidth()/2;
		float gtCancelCenY = btCancel.getHeight()/2;
		float offX = 50; // Separación en x
		float offY = 100; // Separacion en y
		float tCartOffY = 200; // Separación Y del texto del cartel.
		float tBtOffY = 20; // Separación Y del texto dentro del botón.
		
		Camera camara = activity.getEngine().getCamera();
		Sprite bgCartel = new Sprite(camara.getWidth()/2 - bgCenX, 
				camara.getHeight()/2 - bgCenY, 
				bg, activity.getVertexBufferObjectManager());
		
		if(tBAction != null){
			Sprite sBtOk = new Sprite(bg.getWidth() - offX - btOk.getWidth(), 
					bg.getHeight() - offY - btOk.getHeight(), 
					btOk, activity.getVertexBufferObjectManager()){
				@Override
				public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
						float pTouchAreaLocalX, float pTouchAreaLocalY) {
					cartelAction.onAccion();
					final EngineLock engineLock = activity.getEngine().getEngineLock();
					engineLock.lock();
					scena.detachChild(this.getParent());
					/* Now it is save to remove the entity! */
					engineLock.unlock();
					
					scena.clearTouchAreas();
					return true;
				}
			};
			Text tBtOk = new Text(gtOkCenX, tBtOffY, 
					activity.getFontMap().get(Resources.FONT_OPTIONS), 
					tBAction,
					new TextOptions(AutoWrap.WORDS, 
							0,  
							HorizontalAlign.CENTER, 0),
					activity.getVertexBufferObjectManager());
			sBtOk.attachChild(tBtOk);
			bgCartel.attachChild(sBtOk);
			scena.registerTouchArea(sBtOk);
		}

		if(tBCancel != null){
			Sprite sBtCancel = new Sprite(bg.getWidth() - offX * 2 - btOk.getWidth() - btCancel.getWidth(), 
					bg.getHeight() - offY - btCancel.getHeight(), 
					btCancel, activity.getVertexBufferObjectManager()){
				@Override
				public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
						float pTouchAreaLocalX, float pTouchAreaLocalY) {
					final EngineLock engineLock = activity.getEngine().getEngineLock();
					engineLock.lock();
					scena.detachChild(this.getParent());
					/* Now it is save to remove the entity! */
					engineLock.unlock();
					
					scena.clearTouchAreas();
					cartelAction.onCancel();
					return true;
				}
			};
			Text tBtCancel = new Text(gtCancelCenX, tBtOffY, 
					activity.getFontMap().get(Resources.FONT_OPTIONS), 
					tBCancel,
					new TextOptions(AutoWrap.WORDS, 
							0,  
							HorizontalAlign.CENTER, 0),
					activity.getVertexBufferObjectManager());
			sBtCancel.attachChild(tBtCancel);
			bgCartel.attachChild(sBtCancel);
			scena.registerTouchArea(sBtCancel);
		}

		/*
		 * Preparamos los textos.
		 */
		Text tTCartel = new Text(offX, tCartOffY, 
				activity.getFontMap().get(Resources.FONT_OPTIONS), 
				tCartel,
				new TextOptions(AutoWrap.WORDS, 
						bg.getWidth() - offX * 2,  
						HorizontalAlign.CENTER, 0),
				activity.getVertexBufferObjectManager());
		bgCartel.attachChild(tTCartel);
		
		scena.attachChild(bgCartel);
		
		return bgCartel;
	}



	/**
	 * Indica que se ha pulsado la tecla de retroceso.
	 */
	public void keyBack() {
		
		if(sceneHandler.isGame()){
			if(sceneHandler.getGame().hasChildScene()){
				/*
				 * está en el menú pero lo suyo sería comprobarlo de otra
				 * forma, ya que podría ser posible que tuviera varios menus.
				 */
				sceneHandler.getGame().getChildScene().back();
			}else{
				if(opciones.isMultiplayer()){
					loadMenuExitOnline();
				}else{
					loadMenuEndGame();
				}
			}
		}else if(sceneHandler.isOptions()){
			this.loadMenuPrincipalScene();
		}else if(sceneHandler.isMenuPrincipal()){
			loadMenuExit();
		}else if(sceneHandler.isSummary()){
			this.loadMenuPrincipalScene();
		}else if(sceneHandler.isWaitingMultiplayer()){
			//FIXME: REQUIERE PREGUNTAR PARA SALIR DE LA ESPERA. (INFORMAR AL SERVER, ETC)
		}
	}
	
	/**
	 * Método que permite cargar el menu contextual.
	 */
	public void loadMenuContextual() {
		this.sceneMenuContextual = new ContextualGameMenu(this, 
					this.sceneHandler, this.activity.getEngine().getCamera());		
		
		if(false){ // Hablitar si queremos efectos.
			// Creamos algo de efecto en los botones del menú contextual.
			float propAncho = activity.getEngine().getCamera().getWidth()/2;
			float propoAlto = activity.getEngine().getCamera().getWidth()/8;
			
			// Botón de Reset.
			this.sceneMenuContextual.getChildByIndex(0).registerEntityModifier(
					new MoveModifier(1, 0, 
							propAncho-(activity.mMenuResetTextureRegion.getWidth()/2), 
							propoAlto-(activity.mMenuResetTextureRegion.getHeight()/2), 
							propoAlto-(activity.mMenuResetTextureRegion.getHeight()/2)));
			
			// Botón de Menú Principal.
			this.sceneMenuContextual.getChildByIndex(1).registerEntityModifier(
					new MoveModifier(1, activity.getEngine().getCamera().getWidth(), 
							propAncho-(activity.mMenuPrincipalTextureRegion.getWidth()/2), 
							(propoAlto*2)-(activity.mMenuPrincipalTextureRegion.getHeight()/2), 
							(propoAlto*2)-(activity.mMenuPrincipalTextureRegion.getHeight()/2)));
			
			// Botón de Continue.
			this.sceneMenuContextual.getChildByIndex(2).registerEntityModifier(
					new MoveModifier(1, 0, 
							propAncho-(activity.mContinueTextureRegion.getWidth()/2), 
							(propoAlto*3)-(activity.mContinueTextureRegion.getHeight()/2), 
							(propoAlto*3)-(activity.mContinueTextureRegion.getHeight()/2)));
			
			// Botón de Quit.
			this.sceneMenuContextual.getChildByIndex(3).registerEntityModifier(
					new MoveModifier(1, activity.getEngine().getCamera().getWidth(),
							propAncho-(activity.mMenuQuitTextureRegion.getWidth()/2), 
							(propoAlto*4)-(activity.mMenuQuitTextureRegion.getHeight()/2), 
							(propoAlto*4)-(activity.mMenuQuitTextureRegion.getHeight()/2)));
			
		}
		sceneHandler.getGame().setChildScene(sceneMenuContextual, false, true, true);
	}

	/**
	 * Método que permite cargar el menu de salir del juego online.
	 */
	public void loadMenuExitOnline() {
		this.mExitOnline = new ExitOnlineMenu(this, 
				this.activity.getEngine().getCamera());		
		sceneHandler.getGame().setChildScene(mExitOnline, false, true, true);
	}
	
	public void loadMenuExit() {
		this.mExitGame = new ExitGameMenu(this, 
				this.activity.getEngine().getCamera());		
		sceneHandler.getScene().setChildScene(mExitGame, false, true, true);
	}

	/**
	 * Carga la ventana de finalizar partido.
	 */
	private void loadMenuEndGame() {
		this.mEndGame = new EndGameMenu(this, 
				this.activity.getEngine().getCamera());		
		sceneHandler.getScene().setChildScene(mEndGame, false, true, true);
		
	}
	
	/**
	 * Método que se invoca cuando un rival vía internet, abandona la partida.
	 */
	public void rivalAbandona() {
		getTextoMensaje(R.string.tMultiPlayerRivalQuit, 
				activity.getFontMap().get(Resources.FONT_OPTIONS), 3f, 
				new Callback<String>() {
			public void onCallback(String pCallbackValue) {
				finalizarJuego();
			}
		});
	}

	/*
	 * Abandona el juego multiplayer.
	 */
	public void leaveMultiplayer() {
		ServerUtilities.callQuitMatch(multiplayer.getOrderMessage(), multiplayer.getIdClient(), multiplayer.getIdMatch());
		finalizarJuego();
	}

	public SceneHandler getSceneHandler() {
		return sceneHandler;
	}

	/**
	 * Se ha encontrado rival multiplayer, se muestra un cartel
	 * informando de los datos de la partida y se inicia 
	 * una cuenta atrás antes de comenzar el partido.
	 */
	public void beforeMultiplayerMatch() {
		// Eliminamos los posibles temporizadores que pudiran existir.
		sceneHandler.getWaitMultiplayer().clearUpdateHandlers();

		String tCartel = (String) activity.getText(R.string.tMultiPlayerMatch);
		if(multiplayer.isLocal()){
			tCartel = tCartel.replace("#team#", activity.getText(R.string.tLocalTeamLabel));
		}else{
			tCartel = tCartel.replace("#team#", activity.getText(R.string.tVisitantTeamLabel));
		}

		tCartel = tCartel.replace("#time#", activity.getText(R.string.tMultiplayerSome));
		
		final Sprite cartel = getCartel(tCartel, null, null, null, sceneHandler.getWaitMultiplayer());

		// Creamos un nuevo temporizador.
		sceneHandler.getWaitMultiplayer().registerUpdateHandler(
				// Lo ejecutaremos cada segundo, restando así la cantidad de segundos
				// antes de comenzar un partido.
				new TimerHandler(1, true, new ITimerCallback() {
					public void onTimePassed(TimerHandler pTimerHandler) {
						if(multiplayer.updateCountDown() <= 0){
							sceneHandler.getWaitMultiplayer()
								.unregisterUpdateHandler(pTimerHandler);
							
							multiplayer.resetTimeCountDown();
							
							EngineLock lock = activity.getEngine().getEngineLock();
							lock.lock();
							cartel.getParent().detachChild(cartel);
							lock.unlock();
							
							Game.this.loadGameScene();
						}
					}
				}));
	}

	public Hud getHud() {
		return this.hud;
	}

	/**
	 * Método que se encarga de actualizar la posición de las entidades en el campo
	 * @param capsPos
	 * @param ballX
	 * @param ballY
	 */
	public void updateCapsPos(List<Double> capsPos, float ballX,
			float ballY) {
//		boolean moving = true;
//		while (moving){
//			moving = false;
//			for(Chapa chapa : chapas){
//				moving = moving || chapa.getBody().isAwake();
//				if(moving) break;
//			}
//			moving = moving || pelota.getBody().isAwake();
//		}
		pelota.getBody().setLinearVelocity(0, 0);
		pelota.getBody().setAngularVelocity(0);
		pelota.getBody().setTransform((ballX + pelota.getWidth() / 2) / PIXEL_TO_METER_RATIO_DEFAULT, 
				(ballY + pelota.getHeight() / 2) / PIXEL_TO_METER_RATIO_DEFAULT, pelota.getRotation());
		int i = 0;
		for(Chapa chapa : chapas){
			float x = capsPos.get(i++).floatValue();
			float y = capsPos.get(i++).floatValue();
			chapa.getBody().setAngularVelocity(0);
			chapa.getBody().setLinearVelocity(0, 0);
			chapa.getBody().setTransform(
					(x + chapa.getWidth() / 2) / PIXEL_TO_METER_RATIO_DEFAULT,
					(y + chapa.getHeight() / 2) / PIXEL_TO_METER_RATIO_DEFAULT,
					chapa.getRotation());
		}
		enableGame();
	}

	/**
	 * Método que capturas algunos errores del multiplayer
	 * @param e
	 */
	public void onMultiplayerError(Exception e) {
		Debug.d("Se ha producido un error en multiplayer.", e);
		getTextoMensaje(R.string.tMultiplayerError, 
				activity.getFontMap().get(Resources.FONT_OPTIONS), 3, 
				new Callback<String>() {
			public void onCallback(String pCallbackValue) {
				finalizarJuego();
				loadSummaryScene();
			}
		});
	}
}